#include <stdio.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/io.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#include <fcntl.h>
#include <termios.h>

#include "syscore.h"
#include "apis.h"
#include "common.h"
#include "modules.h"
#include "../logServer/clogger.h"
#define PMLogDebug(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_DEBUG, fmt, ##args);
#define PMLogInfo(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_INFO, fmt, ##args);
#define PMLogNotice(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_NOTICE, fmt, ##args);
#define PMLogWarning(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_WARNING, fmt, ##args);
#define PMLogError(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_ERR, fmt, ##args);
#define PMLogCrit(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_CRIT, fmt, ##args);
#define PMLogAlert(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_ALERT, fmt, ##args);
#define PMLogEmerg(fmt, args...) CLogger(MODULE_PM, __FILE__, __LINE__, LOG_EMERG, fmt, ##args);
#define PM_PRINTF                                1

#define BUILD_GSM_SKU                            1
#define NEW_POWER                                1


#define GPIO_BAR                                 0x500
#define GPIO_0_OFFSET                            0x80  /* SUS_GPIO[31:00] OFFSET 0x80 */
#define GPIO_1_OFFSET                            0xA0  /* SUS_GPIO[63:32] OFFSET 0xA0 */
#define GPIO_USE_SELECT_OFFSET                   0x0
#define GPIO_IO_SELECT_OFFSET                    0x4
#define GPIO_LEVEL_OFFSET                        0x8

#define GPIO_IO_MODE_O                           0
#define GPIO_IO_MODE_I                           1

#define GPIO_LEVEL_L                             0
#define GPIO_LEVEL_H                             1

#define port_mask(x)  (1<<(x))

#define out_portb(port, data)                    outb(data, port) 
#define in_portb(port)                           inb(port)
#define out_port(port, data)                     outl(data, port) 
#define in_port(port)                            inl(port) 

#ifdef NEW_POWER                                   
#define GPIO_PIN_IDX_FDD_UNICOM_PA               0x0
#define GPIO_PIN_IDX_FDD_CDMA_PA                 0x1
#define GPIO_PIN_IDX_TDD_CMCC_PA1                0x2
#define GPIO_PIN_IDX_TDD_CMCC_PA2                0x3
#define GPIO_PIN_IDX_GPS                         0x4      
#define GPIO_PIN_IDX_NC                          0x5      
#define GPIO_PIN_IDX_DOOR                        0x6
#define GPIO_PIN_IDX_GRAND                       0x7
#else
#define GPIO_PIN_IDX_CHIP_SCL                    0x0
#define GPIO_PIN_IDX_CHIP_SDA                    0x1
#define GPIO_PIN_IDX_CHIP_LATCH                  0x2
#define GPIO_PIN_IDX_RESERVED1                   0x3
#define GPIO_PIN_IDX_RESERVED2                   0x4
#define GPIO_PIN_IDX_GPS                         0x5
#define GPIO_PIN_IDX_DOOR                        0x6
#define GPIO_PIN_IDX_GRAND                       0x7

#define SHIFT_IO_PIN_PA1_28V_CRTL                0
#define SHIFT_IO_PIN_PA1_12V_CRTL                1
#define SHIFT_IO_PIN_PA2_28V_CRTL                2
#define SHIFT_IO_PIN_PA2_12V_CRTL                3
#define SHIFT_IO_PIN_PA3_28V_CRTL                4
#define SHIFT_IO_PIN_PA3_12V_CRTL                5
#define SHIFT_IO_PIN_PA4_28V_CRTL                6
#define SHIFT_IO_PIN_BBU_RRU_CRTL1               7
#define SHIFT_IO_PIN_BBU_RRU_CRTL2               8
#define SHIFT_IO_PIN_BBU_RRU_CRTL3               9
#define SHIFT_IO_PIN_BBU_RRU_CRTL4               10
#define SHIFT_IO_PIN_BBU_RRU_CRTL5               11
#define SHIFT_IO_PIN_RESERVED1                   12
#define SHIFT_IO_PIN_RESERVED2                   13
#define SHIFT_IO_PIN_RESERVED3                   14
#define SHIFT_IO_PIN_RESERVED4                   15
#endif

#define DOOR_OPEN                                0x1
#define DOOR_CLOSE                               0x0
#define GRAND_LOST                               0x1
#define POWER_ON                                 0x1
#define POWER_OFF                                0x0

typedef enum tag_pm_ret {
    pm_ret_ok = 0,
    pm_ret_nok,
    pm_not_found,
    pm_off,
    pm_not_supported,
    pm_err_param,

} pm_ret_t;


bool stop_pm_process = false;

MODREG mod_pwr_info = {MODULE_PM, false, false, 100, NULL};

/*   pin table           3   4   5   6   7   8   9   10 */  
uint32_t gpio_port[8] = {35, 25, 24, 29, 34, 42, 31, 43};
uint32_t gpio_port_io_select[8] = {0, 0, 0, 0, 0, 0, 1, 1};
uint32_t gpio_port_level[8]     = {0, 0, 0, 0, 0, 0, 0, 0};
#ifdef NEW_POWER
const char *gpio_port_str[8] = {"fdd-1.8-unicom", "fdd-1.8-cdma", "tdd-1.9-cmcc", "tdd-2.6-cmcc", "gps", "NC", "door", "grand"};
#else
const char *gpio_port_str[8] = {"pa1", "pa2", "bb1", "bb2", "bb3", "gps", "door", "grand"};
#endif

uint32_t gpio_group_offset[8] = {0xA0, 0x80, 0x80, 0x80, 0xA0, 0xA0, 0x80, 0xA0};
uint32_t gpio_group_pin_idx[2][4] = { {1, 2, 3, 6}, {0, 4, 5, 7} };
uint32_t gpio_group_level[2] = {0, 0};
bool     gpio_group_save_init_level[2] = {true, true};
static const char *uart_serial_dev[] = {"/dev/ttyS0", "/dev/ttyS1", "/dev/ttyS2", "/dev/ttyS3", "/dev/ttyS4", "/dev/ttyS5"};
bool save_init_level = true;
uint32_t gpio_group0_level = 0;
uint32_t gpio_group1_level = 0;
uint16_t shift_ios = 0;
#ifndef NEW_POWER
static int set_shift_io_level(uint32_t pin, int val);
#endif
uint32_t detect_gpio_input(uint32_t pin_idx)
{
    uint32_t change, cur_level, input_val = 0xFFFFFFFF;
    uint32_t group = gpio_port[pin_idx] / 32;
    uint32_t shift = gpio_port[pin_idx] % 32;

    cur_level = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_LEVEL_OFFSET);
    //printf("%s, gpio group(%d) level(%x) \n", __func__, group, cur_level);

    if( gpio_group_save_init_level[group] )
    {
        gpio_group_level[group] = cur_level;
        gpio_group_save_init_level[group] = false;
    }

    change = ((gpio_group_level[group] ^ cur_level) & (gpio_port_io_select[pin_idx] << shift)) >> shift;
    //printf("%s, gpio group(%d) level change(%u) \n", __func__, group, change);

    if( change )
    {
        input_val = (cur_level & port_mask(shift) ) >> shift;

        PMLogDebug(" %s pin(%u) gpio input value %u",  
            gpio_port_str[pin_idx], gpio_port[pin_idx], input_val);

        gpio_group_level[group] = cur_level;
    }
    return input_val;
}

void set_gpio_level(uint32_t pin_idx, int lvl)
{
    uint32_t old_val, new_val;
    uint32_t group = gpio_port[pin_idx] / 32;

    gpio_port_level[pin_idx] = lvl;

    old_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_LEVEL_OFFSET);
    //printf("%s, set pin(%d) level(%d), old value %x \n", __func__, pin_idx, lvl, old_val);

    new_val = old_val & ~( (1 << (gpio_port[gpio_group_pin_idx[group][0]] % 32)) |
                           (1 << (gpio_port[gpio_group_pin_idx[group][1]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[group][2]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[group][3]] % 32)) );
    //printf("%s, set pin(%d) level(%d), tmp value %x \n", __func__, pin_idx, lvl, new_val);

    new_val |= ( (gpio_port_level[gpio_group_pin_idx[group][0]] << (gpio_port[gpio_group_pin_idx[group][0]] % 32)) |
                 (gpio_port_level[gpio_group_pin_idx[group][1]] << (gpio_port[gpio_group_pin_idx[group][1]] % 32)) |
                 (gpio_port_level[gpio_group_pin_idx[group][2]] << (gpio_port[gpio_group_pin_idx[group][2]] % 32)) |
                 (gpio_port_level[gpio_group_pin_idx[group][3]] << (gpio_port[gpio_group_pin_idx[group][3]] % 32)) );

    out_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_LEVEL_OFFSET, new_val);
    //new_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_LEVEL_OFFSET);
    //printf("%s, set pin(%d) level(%d), new value %x \n", __func__, pin_idx, lvl, new_val);
}

void set_gpio_io_select(int32_t pin_idx, uint32_t mode)
{
    uint32_t old_val, new_val;
    uint32_t group = gpio_port[pin_idx] / 32;

    gpio_port_io_select[pin_idx] = mode;

    old_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), old value %x \n", __func__, pin_idx, mode, old_val);

    new_val = old_val & ~( (1 << (gpio_port[gpio_group_pin_idx[group][0]] % 32)) |
                           (1 << (gpio_port[gpio_group_pin_idx[group][1]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[group][2]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[group][3]] % 32)) );
    //printf("%s, set pin(%d) mode(%d), tmp value %x \n", __func__, pin_idx, mode, new_val);

    new_val |= ( (gpio_port_io_select[gpio_group_pin_idx[group][0]] << (gpio_port[gpio_group_pin_idx[group][0]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[group][1]] << (gpio_port[gpio_group_pin_idx[group][1]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[group][2]] << (gpio_port[gpio_group_pin_idx[group][2]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[group][3]] << (gpio_port[gpio_group_pin_idx[group][3]] % 32)) );

    out_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_IO_SELECT_OFFSET, new_val);
    //new_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), new value %x \n", __func__, pin_idx, mode, new_val);
}

void set_gpio_io_select_default()
{
    uint32_t old_val, new_val;

    old_val = in_port(GPIO_BAR + GPIO_0_OFFSET + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), old value %x \n", __func__, pin_idx, mode, old_val);

    new_val = old_val & ~( (1 << (gpio_port[gpio_group_pin_idx[0][0]] % 32)) |
                           (1 << (gpio_port[gpio_group_pin_idx[0][1]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[0][2]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[0][3]] % 32)) );
    //printf("%s, set pin(%d) mode(%d), tmp value %x \n", __func__, pin_idx, mode, new_val);

    new_val |= ( (gpio_port_io_select[gpio_group_pin_idx[0][0]] << (gpio_port[gpio_group_pin_idx[0][0]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[0][1]] << (gpio_port[gpio_group_pin_idx[0][1]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[0][2]] << (gpio_port[gpio_group_pin_idx[0][2]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[0][3]] << (gpio_port[gpio_group_pin_idx[0][3]] % 32)) );

    out_port(GPIO_BAR + GPIO_0_OFFSET + GPIO_IO_SELECT_OFFSET, new_val);
    //new_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), new value %x \n", __func__, pin_idx, mode, new_val);

    old_val = in_port(GPIO_BAR + GPIO_1_OFFSET + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), old value %x \n", __func__, pin_idx, mode, old_val);

    new_val = old_val & ~( (1 << (gpio_port[gpio_group_pin_idx[1][0]] % 32)) |
                           (1 << (gpio_port[gpio_group_pin_idx[1][1]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[1][2]] % 32)) | 
                           (1 << (gpio_port[gpio_group_pin_idx[1][3]] % 32)) );
    //printf("%s, set pin(%d) mode(%d), tmp value %x \n", __func__, pin_idx, mode, new_val);

    new_val |= ( (gpio_port_io_select[gpio_group_pin_idx[1][0]] << (gpio_port[gpio_group_pin_idx[1][0]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[1][1]] << (gpio_port[gpio_group_pin_idx[1][1]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[1][2]] << (gpio_port[gpio_group_pin_idx[1][2]] % 32)) |
                 (gpio_port_io_select[gpio_group_pin_idx[1][3]] << (gpio_port[gpio_group_pin_idx[1][3]] % 32)) );

    out_port(GPIO_BAR + GPIO_1_OFFSET + GPIO_IO_SELECT_OFFSET, new_val);
    //new_val = in_port(GPIO_BAR + gpio_group_offset[pin_idx] + GPIO_IO_SELECT_OFFSET);
    //printf("%s, set pin(%d) mode(%d), new value %x \n", __func__, pin_idx, mode, new_val);
}

void gpio_init()
{
    uint32_t idx;
    uint32_t shift = 0;

    for( idx = 0; idx < sizeof(gpio_port) / sizeof(uint32_t); ++idx )
    {  
        if( gpio_port[idx] > 31 )
        {
            shift = gpio_port[idx] - 32;
        }
        else
        {
            shift = gpio_port[idx];
        }

        uint32_t old_val = in_port(GPIO_BAR + gpio_group_offset[idx]);
        //printf("%s, using pin(%d) as gpio, %x, %x \n", __func__, idx, old_val, (old_val | (1 << shift)));
	  
        out_port(GPIO_BAR + gpio_group_offset[idx], (old_val | (1 << shift)));

        //uint32_t new_val = in_port(GPIO_BAR + gpio_group_offset[idx]);
        //printf("%s, using pin(%d) as gpio, %x \n", __func__, idx, new_val);
	}

    set_gpio_io_select_default();
    
#ifdef NEW_POWER 
    set_gpio_level(GPIO_PIN_IDX_FDD_UNICOM_PA, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_FDD_CDMA_PA, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_TDD_CMCC_PA1, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_TDD_CMCC_PA2, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_NC, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_GPS, GPIO_LEVEL_L);
    
    sleep(1);
#ifdef BUILD_GSM_SKU
    set_gpio_level(GPIO_PIN_IDX_FDD_UNICOM_PA, GPIO_LEVEL_H);
    set_gpio_level(GPIO_PIN_IDX_FDD_CDMA_PA, GPIO_LEVEL_H);
#endif

#else   
//    set_gpio_level(GPIO_PIN_IDX_PA1, GPIO_LEVEL_L);
//    set_gpio_level(GPIO_PIN_IDX_PA2, GPIO_LEVEL_L);
//    set_gpio_level(GPIO_PIN_IDX_BB1, GPIO_LEVEL_L);
//    set_gpio_level(GPIO_PIN_IDX_BB2, GPIO_LEVEL_L);
//    set_gpio_level(GPIO_PIN_IDX_BB3, GPIO_LEVEL_L);

    set_gpio_level(GPIO_PIN_IDX_GPS, GPIO_LEVEL_L);

    sleep(1);
	/*
#ifdef BUILD_GSM_SKU
    set_shift_io_level(SHIFT_IO_PIN_BBU_RRU_CRTL3, GPIO_LEVEL_H);
    set_shift_io_level(SHIFT_IO_PIN_PA1_12V_CRTL, GPIO_LEVEL_H);
    set_shift_io_level(SHIFT_IO_PIN_PA1_28V_CRTL, GPIO_LEVEL_H);
#endif
*/
#endif

    set_gpio_level(GPIO_PIN_IDX_GPS, GPIO_LEVEL_H);
    PMLogDebug( "init GPIO done ");
}
#ifndef NEW_POWER
void shift_io_set(uint16_t val)
{
    int i;

    set_gpio_level(GPIO_PIN_IDX_CHIP_SCL, 0);
    set_gpio_level(GPIO_PIN_IDX_CHIP_SDA, 0);
    set_gpio_level(GPIO_PIN_IDX_CHIP_LATCH, 0);

    /* MSB */
    for(i=16; i!=0; i--)
    {
        if(val & 0x8000)
        {
            set_gpio_level(GPIO_PIN_IDX_CHIP_SDA, 1);
        }
        else
        {
            set_gpio_level(GPIO_PIN_IDX_CHIP_SDA, 0);
        }
        usleep(1);
        set_gpio_level(GPIO_PIN_IDX_CHIP_SCL, 1);
        usleep(10);
        set_gpio_level(GPIO_PIN_IDX_CHIP_SCL, 0);
        usleep(10);
        val = val << 1;
    }
    set_gpio_level(GPIO_PIN_IDX_CHIP_LATCH, 1);
    usleep(10);
    set_gpio_level(GPIO_PIN_IDX_CHIP_LATCH, 0);
    set_gpio_level(GPIO_PIN_IDX_CHIP_SDA, 0);
}

/**
 * @name: set_shift_io_level
 * @input: 
 *      pin
 *           SHIFT_IO_PIN_PA1_28V_CRTL                0
 *           SHIFT_IO_PIN_PA1_12V_CRTL                1
 *           SHIFT_IO_PIN_PA2_28V_CRTL                2
 *           SHIFT_IO_PIN_PA2_12V_CRTL                3
 *           SHIFT_IO_PIN_PA3_28V_CRTL                4
 *           SHIFT_IO_PIN_PA3_12V_CRTL                5
 *           SHIFT_IO_PIN_PA4_28V_CRTL                6
 *           SHIFT_IO_PIN_BBU_RRU_CRTL1               7
 *           SHIFT_IO_PIN_BBU_RRU_CRTL2               8
 *           SHIFT_IO_PIN_BBU_RRU_CRTL3               9
 *           SHIFT_IO_PIN_BBU_RRU_CRTL4               10
 *           SHIFT_IO_PIN_BBU_RRU_CRTL5               11
 *           SHIFT_IO_PIN_RESERVED1                   12
 *           SHIFT_IO_PIN_RESERVED2                   13
 *           SHIFT_IO_PIN_RESERVED3                   14
 *           SHIFT_IO_PIN_RESERVED4                   15
 *      val
 *          0 -> low level
 *          1 -> high level
 *
 * @return: 0 if pin is in rang, else -1
 *
 */

int set_shift_io_level(uint32_t pin, int val)
{
    static uint16_t last_ios = 0;
    int ret = 0;

    if(pin < sizeof(shift_ios)*8)
    {
        if(val) 
        {
            shift_ios |= (uint16_t)1<<pin;
        }
        else
        {
            shift_ios &= (uint16_t)(~(uint16_t)(1<<pin));
        }
    }
    else
    {
        ret = -1;
    }


    if(last_ios != shift_ios)
    {
        last_ios = shift_ios;
        shift_io_set(shift_ios);
    }

   return ret; 
}

int get_shift_io_level(uint32_t pin)
{
    int val = 0;

    if(pin < sizeof(shift_ios)*8)
    {
        val = shift_ios & (uint16_t)(1<<pin);
    }

    return val;
}
#endif

void reportPwrCmdResult(uint16_t dst_id, uint16_t cmd_code, uint16_t cmd_idx, uint32_t result, uint32_t err_code)
{
    char msg_buf[MSG_BUFF_SIZE] = {0};
    MSGHEADER *msg_header = (MSGHEADER *)msg_buf;
    void* msg_payload = (void*)(msg_buf + sizeof(MSGHEADER));

    msg_header->srcID = MODULE_PM;
    msg_header->dstID = dst_id;
    msg_header->msgType = MSG_TYPE_COMMAND;

    PCMDHEADER cmd = (PCMDHEADER)msg_payload;
    cmd->cmdType = CMD_SET_RESPONSE;
    cmd->cmdCode = cmd_code;
    cmd->cmdIndex = cmd_idx;
    cmd->cmdLength = 0;

    msg_header->msgLength = sizeof(CMDHEADER) + sizeof(MSGHEADER);
    uint8_t *ptr = (uint8_t *)msg_payload;
    ptr += sizeof(CMDHEADER);

    if( PARA_VALUE_RESULT_FAILED == result )
    {
        *(uint32_t*)ptr = PARA_TYPE_RESULT;
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = sizeof(uint32_t);
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = PARA_VALUE_RESULT_FAILED;
        ptr += sizeof(uint32_t) + sizeof(uint32_t);
        cmd->cmdLength += sizeof(uint32_t) * 4;

        *(uint32_t*)ptr = PARA_TYPE_ERROR_CODE;
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = sizeof(uint32_t);
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = PARA_VALUE_ERROR_PM_OK + err_code;
        cmd->cmdLength += sizeof(uint32_t) * 4;
    }
    else
    {
        *(uint32_t*)ptr = PARA_TYPE_RESULT;
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = sizeof(uint32_t);
        ptr += sizeof(uint32_t);
        *(uint32_t*)ptr = result;
        cmd->cmdLength += sizeof(uint32_t) * 4;
    }

    msg_header->msgLength += cmd->cmdLength;

    if( RC_OK != CM_sendMsg(msg_header, msg_payload) )
    {
        PMLogError(" can't report msg ");
    }
}



static int32_t open_uart_serial_port(int32_t port_num)
{
    int32_t ret = 0;

    int32_t fd = open(uart_serial_dev[port_num], O_RDWR | O_NOCTTY | O_NDELAY);
    if( -1 == fd ) 
    {
        PMLogWarning("can not open uart");
        return (-1);
    }

    PMLogDebug("Open serial sort %s successfully", uart_serial_dev[port_num]);

    /* set blocking state */
    ret = fcntl(fd, F_SETFL , 0);
    if( ret < 0 )
    {
        PMLogNotice("fcntl operation on fd(%d) failed\n", fd);
    }

    /* test tty */
    if( isatty(STDIN_FILENO) == 0 )
    {
        PMLogNotice("standard input is not a terminal device");
    }
    else
    {
        PMLogDebug("standard input is a tty");
    }

    PMLogDebug(" Open %s done, get fd = %d.", uart_serial_dev[port_num], fd);
    return fd;
}

static int32_t set_uart_serial_opt(int32_t fd, int32_t nSpeed, int32_t nBits, char nEvent, int32_t nStop)
{
    struct termios newtio, oldtio;
    if( tcgetattr(fd, &oldtio) != 0 )
    {                       
        PMLogNotice("Get fd(%d) attribute failed", fd);                             
        return -1;
    }

    bzero(&newtio, sizeof (newtio));
    /* Active CLOCAL and CREAD */
    newtio.c_cflag |= CLOCAL | CREAD;

    switch( nSpeed )
    {
    case 2400:
        cfsetispeed(&newtio, B2400);
        cfsetospeed(&newtio, B2400);
        break;
    case 4800:
        cfsetispeed(&newtio, B4800);
        cfsetospeed(&newtio, B4800);
        break;
    case 9600:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    case 19200:
        cfsetispeed(&newtio, B19200);
        cfsetospeed(&newtio, B19200);
        break;
    case 115200:
        cfsetispeed(&newtio, B115200);
        cfsetospeed(&newtio, B115200);
        break;
    case 460800:
        cfsetispeed(&newtio, B460800);
        cfsetospeed(&newtio, B460800);
        break;
    default:
        cfsetispeed(&newtio, B9600);
        cfsetospeed(&newtio, B9600);
        break;
    }

    /* Set data bit */
    newtio.c_cflag &= ~CSIZE;
    switch( nBits ) 
    {
    case 7:
        newtio.c_cflag |= CS7;
        break;
    case 8:
        newtio.c_cflag |= CS8;
        break;
    }

    switch( nEvent )
    {
    case 'O':
        newtio.c_cflag |= PARENB;
        newtio.c_cflag |= PARODD;
        newtio.c_iflag |= (INPCK | ISTRIP);
        break;
    case 'E':
        newtio.c_iflag |= (INPCK | ISTRIP);
        newtio.c_cflag |= PARENB;
        newtio.c_cflag &= ~PARODD;
        break;
    case 'N':
        newtio.c_cflag &= ~PARENB;
        break;
    }
  
    if( nStop == 1 )
    {
        newtio.c_cflag &= ~CSTOPB;
    }
    else if (nStop == 2)
    {
        newtio.c_cflag |= CSTOPB;
    }

    newtio.c_cc[VTIME] = 0;
    newtio.c_cc[VMIN] = 0;

    /* flush buffered data which are not sent yet */
    tcflush(fd, TCIFLUSH);

    /* Apply the new setting now */
    if( (tcsetattr(fd, TCSANOW, &newtio)) != 0 )
    { 
        PMLogNotice("Set fd(%d) attribute failed", fd);
        return -1;
    }

    PMLogDebug("Set serial port option done");
    return 0;
}

static bool init_uart_serial(int32_t port_num, int32_t *fd)
{
    *fd = open_uart_serial_port(port_num);
    if( *fd < 0 )
    {
        PMLogNotice(" Open serial port failed \n");
        return false;
    }

    return true;
}

void rebootDevice(uint16_t dst_id, uint16_t cmd_code, uint16_t cmd_idx) 
{
    int32_t fd = 0;
    if (!init_uart_serial(3, &fd))
    {
        PMLogNotice("init uart failed, can not reboot device ");
        reportPwrCmdResult(dst_id, cmd_code, cmd_idx,
                    PARA_VALUE_RESULT_FAILED, PARA_VALUE_ERROR_GENERAL_ERROR);
        return;
    }

    if( set_uart_serial_opt(fd, 9600, 8, 'N' , 1) < 0 )
    {
        PMLogNotice("Set serial option failed, can not reboot device");
        reportPwrCmdResult(dst_id, cmd_code, cmd_idx,
                    PARA_VALUE_RESULT_FAILED, PARA_VALUE_ERROR_GENERAL_ERROR);
        return;
    }
    reportPwrCmdResult(dst_id, cmd_code, cmd_idx,
                    PARA_VALUE_RESULT_SUCCESS, PARA_VALUE_ERROR_NO_ERROR);

    sleep(2); //wait to send rsp to server
    PMLogNotice("start to reboot device");
    int32_t i = 5000, ret = 0;
    char rebootMsg[2] = {0};
    rebootMsg[0] = 0x00;
    while(i >0)
    {
        ret = write(fd, rebootMsg, 1);
        if (ret < 0)
        {
            PMLogNotice("write error");
        }
        usleep(500);
        i--;
    }
    close(fd);
    
}

void reportPmAlarm(uint32_t alarm)
{
    char msg_buf[MSG_BUFF_SIZE] = {0};
    MSGHEADER *msg_header = (MSGHEADER *)msg_buf;
    void* msg_payload = (void*)(msg_buf + sizeof(MSGHEADER));

    msg_header->srcID = MODULE_PM;
    msg_header->dstID = MODULE_INTF;
    msg_header->mType = get_mtype(msg_header->dstID);
    msg_header->msgType = MSG_TYPE_COMMAND;

    PCMDHEADER cmd = (PCMDHEADER)msg_payload;
    cmd->cmdType = CMD_EVENT;
    cmd->cmdCode = alarm;
    cmd->cmdIndex = 0;
    cmd->cmdLength = 0;

    msg_header->msgLength = sizeof(CMDHEADER) + sizeof(MSGHEADER);

    if( RC_OK != CM_sendMsg(msg_header, msg_payload) )
    {
        PMLogError("can't report msg ");
    }

    if (alarm == CMD_EVT_ALARM_PM_GRAND_LOST)
    {
        msg_header->dstID = MODULE_GPS;
        if( RC_OK != CM_sendMsg(msg_header, msg_payload) )
        {
            PMLogError("can't report msg ");
        }
    }
} 

void reportHeartBeat()
{
    U32 rc = PARA_VALUE_SYSMONITOR_MODULE_OK;
    CM_SendMsg_1Para(MODULE_SYSMONITOR, MODULE_PM, MSG_TYPE_COMMAND, CMD_EVENT, CMD_CODE_SYSMONITOR_HEARTBEAT, 0,
        PARA_TYPE_SYSMONITOR_MODULE_STATE,sizeof(U32), (U8*)&rc);   
}

void tlvPmMsgHdlr(PMSGHEADER header, void* payload)
{
    pm_ret_t ret = pm_ret_ok;
    uint32_t len = header->msgLength, cmd_len = 0;
    uint8_t *ptr = (uint8_t*)payload;
    uint16_t srcId = header->srcID;
	uint8_t gpio_pin = 0;
	uint8_t bbuId = 0;

    if( MODULE_PM != header->dstID )
    {
        PMLogNotice("received a wrong tlv dst id = %d ", header->dstID);
        return;
    }

    if( MSG_TYPE_COMMAND == header->msgType )
    {
        PCMDHEADER cmd = (PCMDHEADER)ptr;
        ptr += sizeof(CMDHEADER);
        len -= sizeof(CMDHEADER);

        cmd_len = cmd->cmdLength;

        if( CMD_SET == cmd->cmdType )
        {
            switch( cmd->cmdCode ) {
            case CMD_CODE_PM_POWERON:
                {
                    if( cmd_len >= sizeof(PARAM) )
                    {
                        PARAM *tlv = (PARAM *)ptr;

                        if( PARA_TYPE_PM_GPIO_CTRL_PIN == tlv->paraType )
                        {
                            if( 1 == tlv->paraLength )
                            {
                                bbuId = *tlv->paraValue;
                                PMLogNotice("rcv power on bbu is : %d ", bbuId);

#ifdef NEW_POWER
								switch (bbuId)
								{
									case 7:
                                    case 3:
                                    case 12:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD1;
											break;
									case 6:
                                    case 4:
                                    case 10:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD2;
											break;
									case 2:
                                    case 5:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD1;
											break;
									case 1:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD2;
											break;
                                    case 8: 
                                    case 11:
                                            gpio_pin = PARA_VALUE_PM_GPIO_PIN_GSM;
                                            break;

									default :
				 							PMLogNotice("Unknown bbuId:%d", bbuId);
            								break;
								}
								if( gpio_pin < 1 || gpio_pin > 12 )                   
#else
                                if( gpio_pin < 1 || gpio_pin > 16 )
#endif
                                {
                                    PMLogInfo("recv wrong tlv, pin value=%u",  gpio_pin);
                                    ret = pm_err_param;
                                }
                                else
                                {
                                    PMLogInfo("set pin(%u) level high",  gpio_pin);
#ifdef NEW_POWER
                                    set_gpio_level(gpio_pin - 1, GPIO_LEVEL_H);
#else
                                    set_shift_io_level(gpio_pin - 1, GPIO_LEVEL_H);
#endif
                                }
                            }
                            else
                            {
                                ret = pm_err_param;
                            }
                        }
                        else
                        {
                            ret = pm_not_supported;
                        }
                    }
                    else
                    {
                        ret = pm_err_param;
                    }
                }
                break;

            case CMD_CODE_PM_SHUTDOWN:
                {
                    if( cmd_len >= sizeof(PARAM) )
                    {
                        PARAM *tlv = (PARAM *)ptr;

                        if( PARA_TYPE_PM_GPIO_CTRL_PIN == tlv->paraType )
                        {
                            if( 1 == tlv->paraLength )
                            {
								
                                bbuId = *tlv->paraValue;
                                PMLogNotice("rcv shutdown bbu is : %d ", bbuId);

#ifdef NEW_POWER
								switch (bbuId)
								{
									case 7:
                                    case 3:
                                    case 12:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD1;
											break;
									case 6:
                                    case 4:
                                    case 10:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD2;
											break;
									case 2:
                                    case 5:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD1;
											break;
									case 1:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD2;
											break;
                                    case 8: 
                                    case 11:
                                            gpio_pin = PARA_VALUE_PM_GPIO_PIN_GSM;
                                            break;

									default :
				 							PMLogNotice("Unknown bbuId:%d", bbuId);
            								break;
								}
								if( gpio_pin < 1 || gpio_pin > 12 )                 
#else
                                if( gpio_pin < 1 || gpio_pin > 16 )
#endif
                                {
                                    PMLogInfo("recv wrong tlv, pin value=%u",  gpio_pin);
                                    ret = pm_err_param;
                                }
                                else
                                {
                                    PMLogInfo("set pin(%u) level low",  gpio_pin);
#ifdef NEW_POWER
                                    set_gpio_level(gpio_pin - 1, GPIO_LEVEL_L);
#else
                                    set_shift_io_level(gpio_pin - 1, GPIO_LEVEL_L);
#endif
                                }
                            }
                            else
                            {
                                ret = pm_err_param;
                            }
                        }
                        else
                        {
                            ret = pm_not_supported;
                        }
                    }
                    else
                    {
                        ret = pm_err_param;
                    }
                }
                break;
            case CMD_CODE_PM_RESTART:
                {
                    if( cmd_len >= sizeof(PARAM) )
                    {
                        PARAM *tlv = (PARAM *)ptr;

                        if( PARA_TYPE_PM_GPIO_CTRL_PIN == tlv->paraType )
                        {
                            if( 1 == tlv->paraLength )
                            {
															
                                bbuId = *tlv->paraValue;
                                PMLogNotice("rcv reboot bbu is : %d ", bbuId);
#ifdef NEW_POWER
								switch (bbuId)
								{
									case 7:
                                    case 3:
                                    case 12:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD1;
											break;
									case 6:
                                    case 4:
                                    case 10:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_FDD2;
											break;
									case 2:
                                    case 5:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD1;
											break;
									case 1:
											gpio_pin = PARA_VALUE_PM_GPIO_PIN_TDD2;
											break;
                                    case 8: 
                                    case 11:
                                            gpio_pin = PARA_VALUE_PM_GPIO_PIN_GSM;
                                            break;

									default :
				 							PMLogNotice("Unknown bbuId:%d", bbuId);
            								break;
								}
								if( gpio_pin < 1 || gpio_pin > 12 )                  
#else
                                if( gpio_pin < 1 || gpio_pin > 16 )
#endif
                                {
                                    PMLogInfo("recv wrong tlv, pin value=%u", gpio_pin);
                                    ret = pm_err_param;
                                }
                                else
                                {
#ifdef NEW_POWER
                                    set_gpio_level(gpio_pin - 1, GPIO_LEVEL_L);
                                    sleep(1);
                                    set_gpio_level(gpio_pin - 1, GPIO_LEVEL_H);
#else
                                    set_shift_io_level(gpio_pin - 1, GPIO_LEVEL_L);
                                    sleep(1);
                                    set_shift_io_level(gpio_pin - 1, GPIO_LEVEL_H);
#endif
                                }
                            }
                            else
                            {
                                ret = pm_err_param;
                            }
                        }
                        else
                        {
                            ret = pm_not_supported;
                        }
                    }
                    else
                    {
                        ret = pm_err_param;
                    }
                }
                break;
            case CMD_CODE_SYS_REBOOT:
                 set_gpio_level(PARA_VALUE_PM_GPIO_PIN_FDD1 - 1, GPIO_LEVEL_L);
                 set_gpio_level(PARA_VALUE_PM_GPIO_PIN_FDD2 - 1, GPIO_LEVEL_L);
                 set_gpio_level(PARA_VALUE_PM_GPIO_PIN_TDD1 - 1, GPIO_LEVEL_L);
                 set_gpio_level(PARA_VALUE_PM_GPIO_PIN_TDD2 - 1, GPIO_LEVEL_L);
                 set_gpio_level(PARA_VALUE_PM_GPIO_PIN_GPS - 1, GPIO_LEVEL_L);
                 ret = pm_ret_ok;
                 break;
   
            case CMD_CODE_PM_RESET_GPS:
                {
                    set_gpio_level(PARA_VALUE_PM_GPIO_PIN_GPS - 1, GPIO_LEVEL_L);
                    sleep(1);
                    set_gpio_level(PARA_VALUE_PM_GPIO_PIN_GPS - 1, GPIO_LEVEL_H);
                }
                break;

            case CMD_CODE_PM_REBOOT_DEVICE:
                rebootDevice(srcId, cmd->cmdCode, cmd->cmdIndex); 
                return;

#ifdef MODULE_TEST
//here is test heartbeat
        case CMD_CODE_SYSMONITOR_TEST_MODULE:
            PMLogError("----- test module power ----- %04x", CMD_CODE_SYSMONITOR_TEST_MODULE);
            while(1)
            {

            }
            break;
#endif
            default:
                PMLogNotice("msg cmd %d not supported ",  cmd->cmdCode);
                ret = pm_not_supported;
                break;
            }

            if( pm_ret_ok == ret )
            {
                reportPwrCmdResult(srcId, cmd->cmdCode, cmd->cmdIndex,
                    PARA_VALUE_RESULT_SUCCESS, PARA_VALUE_ERROR_NO_ERROR);
            }
            else
            {
                reportPwrCmdResult(srcId, cmd->cmdCode, cmd->cmdIndex,
                    PARA_VALUE_RESULT_FAILED, ret);
            }
        }
        else if( CMD_GET == cmd->cmdType )
        {
            switch( cmd->cmdCode ) {

            default:
                PMLogInfo("%s, msg cmd %d not supported \n", __func__, cmd->cmdCode);
                break;
            }
            
            if( pm_ret_ok != ret )
            {
                reportPwrCmdResult(srcId, cmd->cmdCode, cmd->cmdIndex, PARA_VALUE_RESULT_FAILED, ret);
            }
        }
        else if( CMD_EVENT == cmd->cmdType )
        {
            switch( cmd->cmdCode ) {

            default:
                PMLogInfo(" msg cmd %d not supported \n",  cmd->cmdCode);
                break;
            }
        }
        else
        {
            PMLogInfo("cmd type %d not supported \n",  cmd->cmdType);
        }
    }
    else if( MSG_TYPE_INTERNAL == header->msgType )
    {

    }
    else
    {
        PMLogNotice(" msg type %d not supported \n",  header->msgType);
    }
}

static void* pwrtask_main(void *arg __attribute__((unused)))
{
    pthread_setname_np(pthread_self(), "pwrtask");

    if( iopl(3) < 0 )
    {

        PMLogError("iopl error %d(%s) \n", errno, strerror(errno));

        pthread_exit((void*)0);
    }

	/* set all pin's function as GPIO */
	gpio_init();

    char msg_buf[MSG_BUFF_SIZE] = {0};
    MSGHEADER *msg_header = (MSGHEADER *)msg_buf;
    void* msg_payload = (void*)(msg_buf + sizeof(MSGHEADER));
    while( ! stop_pm_process )
    {

        /* get msg from system process */
        if( RC_OK == MSG_read_nonblock(MODULE_PM, msg_header, msg_payload) )
        {
            tlvPmMsgHdlr(msg_header, msg_payload);
        }

        uint32_t value = detect_gpio_input(PARA_VALUE_PM_GPIO_PIN_POWER - 1);
        if( POWER_ON == value )
        {
            reportPmAlarm(CMD_EVT_ALARM_PM_POWER_ON);
            PMLogWarning("external power on using external power");

        }
        else if( POWER_OFF == value )
        {
            reportPmAlarm(CMD_EVT_ALARM_PM_POWER_OFF);
            PMLogWarning("external power off using local power supply");
        }

        value = detect_gpio_input(GPIO_PIN_IDX_GRAND);
        if( GRAND_LOST == value )
        {
            reportPmAlarm(CMD_EVT_ALARM_PM_GRAND_LOST);
            PMLogWarning("grand lost device is moving");
        }
        reportHeartBeat();
        sleep(2);
    }
    
    PMLogInfo("pwrtask thread exit ");
    pthread_exit((void*)0);
}

RESULT pwrtask_init()
{
    pthread_t thread;

    if( RC_OK != MOD_register(&mod_pwr_info) )
    {
        printf("Can't register module with syscore\n");
        return RC_FAIL;
    }

    pthread_create(&thread, NULL, pwrtask_main, 0);

    return RC_OK;
}

#if 0
int main()
{
    void *arg = NULL;

    if( RC_OK != MOD_register(&mod_pwr_info) )
    {
        printf("Can't register module with syscore\n");
        return -1;
    }

    pwrtask_main(arg);

    return 0;
}
#endif
